﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MergeTool.ClassLib.Common;
using MergeTool.ClassLib.Configuration;
using Microsoft.TeamFoundation.Framework.Client.Catalog.Objects;
using Microsoft.TeamFoundation.VersionControl.Client;

namespace MergeTool.ClassLib
{
    public class BranchManager
    {
        private readonly Settings _settings;
        private readonly TfsConnection _connection;

        public BranchManager()
        {
            this._settings = Settings.GetInstance();
            this._connection = TfsConnection.GetInstance();
        }

        public IEnumerable<BranchObject> GetAllNonDeletedBranchObjects()
        {
            var branchObjects = _connection.GetVersionControlServer().QueryRootBranchObjects(RecursionType.Full).Where(br => br.Properties.RootItem.Item.StartsWith(_connection.GetTeamProject().ServerItem + "/") && !br.Properties.RootItem.IsDeleted);
            return branchObjects;
        }

        public bool IsEqualVersionControlStrings(string serverItem, string configItem)
        {
            const string sourceControlSeparator = "/";
            if (!configItem.Contains(sourceControlSeparator))
            {
                return String.Equals(serverItem.Substring(serverItem.LastIndexOf(sourceControlSeparator) + 1),
                    configItem, StringComparison.InvariantCultureIgnoreCase);
            }
            else
            {
                return serverItem.EndsWith(configItem, StringComparison.InvariantCultureIgnoreCase);
            }
        }

        public List<string> GetUnavailableBranches(MergeApproach mergeApproach)
        {
            var branchObjects = GetAllNonDeletedBranchObjects();
            var unavailableBranchPairs = new List<string>();

            var branches = branchObjects as IList<BranchObject> ?? branchObjects.ToList();
            foreach (var branchPair in mergeApproach.BranchPairs)
            {
                var validBranchesFrom = branches.Where(br => IsEqualVersionControlStrings(br.Properties.RootItem.Item, branchPair.BranchNameFrom));
                var validBranchesTo = branches.Where(br => IsEqualVersionControlStrings(br.Properties.RootItem.Item, branchPair.BranchNameTo));
                if (!validBranchesFrom.Any())
                    unavailableBranchPairs.Add(branchPair.BranchNameFrom);

                if (!validBranchesTo.Any())
                    unavailableBranchPairs.Add(branchPair.BranchNameTo);
            }
            return unavailableBranchPairs;
        }

        public List<string> GetNonUniqueBranches(MergeApproach mergeApproach)
        {
            var branchObjects = GetAllNonDeletedBranchObjects();
            var duplicateBranches = new List<string>();

            var branches = branchObjects as IList<BranchObject> ?? branchObjects.ToList();
            foreach (var branchPair in mergeApproach.BranchPairs)
            {
                var nonUniqueCandidatesFrom =
                    branches.Where(br => IsEqualVersionControlStrings(br.Properties.RootItem.Item, branchPair.BranchNameFrom));

                var nonUniqueCandidatesTo =
                     branches.Where(br => IsEqualVersionControlStrings(br.Properties.RootItem.Item, branchPair.BranchNameTo));

                if (nonUniqueCandidatesFrom.Count() > 1)
                    duplicateBranches.Add(branchPair.BranchNameFrom);

                if (nonUniqueCandidatesTo.Count() > 1)
                    duplicateBranches.Add(branchPair.BranchNameTo);
            }
            return duplicateBranches;
        }
        
        /// <summary>
        /// string = MergeApproach
        /// </summary>
        /// <returns>string = MergeApproach</returns>
        public List<BranchPair> GetBaselessMergeBranches(MergeApproach mergeApproach)
        {
            var branchObjects = GetAllNonDeletedBranchObjects();
            var baselessBranches = new List<BranchPair>();
            
            var mergeApproachBranches = mergeApproach.BranchPairs.OrderBy(br => br.Order).ToList();

            
            for (int i = 0; i < mergeApproachBranches.Count; i++)
            {
                var testForBaselessMerge = from bo in branchObjects
                                           where IsEqualVersionControlStrings(bo.Properties.RootItem.Item, mergeApproachBranches[i].BranchNameFrom) &&
                                                       ((bo.Properties.ParentBranch != null && IsEqualVersionControlStrings(bo.Properties.ParentBranch.Item, mergeApproachBranches[i].BranchNameTo)) ||
                                                       (bo.ChildBranches != null && bo.ChildBranches.Any(child => IsEqualVersionControlStrings(child.Item, mergeApproachBranches[i].BranchNameTo))))
                                               select bo;

                if (!testForBaselessMerge.Any())
                {
                    baselessBranches.Add(new BranchPair()
                    {
                        BranchNameFrom = mergeApproachBranches[i].BranchNameFrom, BranchNameTo = mergeApproachBranches[i].BranchNameTo
                    });
                }
            }



            /*
                var e = mergeApproachBranches.GetEnumerator();

                e.MoveNext();
                Branch currentBranch = e.Current;
                while (e.MoveNext())
                {
                    Branch nextBranch = e.Current;

                    var testForBaselessMerge = from bo in branchObjects
                                               where bo.Properties.RootItem.Item.EndsWith(currentBranch.BranchName) &&
                                                       ((bo.Properties.ParentBranch != null && bo.Properties.ParentBranch.Item.EndsWith(nextBranch.BranchName)) ||
                                                       (bo.ChildBranches != null && bo.ChildBranches.Any(child => child.Item.EndsWith(nextBranch.BranchName))))
                                               select bo;
                    if (!testForBaselessMerge.Any())
                    {
                        baselessBranches.Add(new Tuple<Branch, Branch>(currentBranch, nextBranch));
                    }
                    currentBranch = e.Current;
                }
            */
            //branchObjects = from br in branchObjects where   ;
            return baselessBranches;
        }

        /// <summary>
        /// returns all paths that are valid for the given merge approach, paths are in server format string
        /// </summary>
        /// <param name="mergeApproach">Approach from Settings class</param>
        /// <returns></returns>
        public List<BranchPair> GetFullBranchPathForMergeApproach(string mergeApproach)
        {
            List<BranchPair> tempValueBranches = null;
            var fullPathBranches = new List<BranchPair>();
            var mergeApproachBranchPairs =
                _settings.MergeApproaches.Where(ma => ma.Name.Equals(mergeApproach)).Select(ma => ma.BranchPairs);

            var branchPairs = mergeApproachBranchPairs.ToArray()[0];

            tempValueBranches = branchPairs.Select(br => br).OrderBy(br => br.Order).ToList();

            for (int i = 0; i < tempValueBranches.Count; i++)
            {
                fullPathBranches.Add(new BranchPair()
                {
                    BranchNameFrom = GetAllNonDeletedBranchObjects()
                        .Where(br => IsEqualVersionControlStrings(br.Properties.RootItem.Item, tempValueBranches.ElementAt(i).BranchNameFrom))
                        .Select(br => br.Properties.RootItem.Item.ToString(CultureInfo.InvariantCulture)).FirstOrDefault(),
                    BranchNameTo = GetAllNonDeletedBranchObjects()
                        .Where(br => IsEqualVersionControlStrings(br.Properties.RootItem.Item, tempValueBranches.ElementAt(i).BranchNameTo))
                        .Select(br => br.Properties.RootItem.Item.ToString(CultureInfo.InvariantCulture)).FirstOrDefault(),
                         MergeOptionString = tempValueBranches.ElementAt(i).MergeOptionString,
                         Order = tempValueBranches.ElementAt(i).Order,
                         CheckinCommentPattern = tempValueBranches.ElementAt(i).CheckinCommentPattern,
                         ChangesetIdToStartFrom = tempValueBranches.ElementAt(i).ChangesetIdToStartFrom
                });
            }
            //var retBranches = new List<Tuple<BranchPair, BranchPair>>();
            /*
            for (int i = 0; i < fullPathBranches.Count; i++)
            {
                retBranches.Add(new Tuple<BranchPair, BranchPair>(fullPathBranches[i++], fullPathBranches[i]));
            }*/

            return fullPathBranches;
        }

        public string GetFullPathForBranch(string path)
        {
            return GetAllNonDeletedBranchObjects()
                .Where(br => IsEqualVersionControlStrings(br.Properties.RootItem.Item, path))
                .Select(br => br.Properties.RootItem.Item.ToString(CultureInfo.InvariantCulture)).FirstOrDefault();
        }
    }
}
